package epam.marchenko.library.controller;

import java.util.Date;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import epam.marchenko.library.domain.Book;
import epam.marchenko.library.domain.BookClass;
import epam.marchenko.library.domain.IncomeRecord;
import epam.marchenko.library.domain.OutcomeRecord;
import epam.marchenko.library.domain.User;
import epam.marchenko.library.services.BookService;
import epam.marchenko.library.services.IncomeRecordService;
import epam.marchenko.library.services.OutcomeRecordService;
import epam.marchenko.library.services.UserService;

/**
 * Handles requests for the application home page.
 */
@Controller
@RequestMapping("/admin")
public class AdminController {

	private static final Logger logger = LoggerFactory
			.getLogger(AdminController.class);

	IncomeRecordService incomeService;
	OutcomeRecordService outcomeService;
	UserService userService;
	BookService bookService;

	@Autowired
	public AdminController(IncomeRecordService incomeService,
			OutcomeRecordService outcomeService, UserService userService,
			BookService bookService) {
		this.incomeService = incomeService;
		this.outcomeService = outcomeService;
		this.userService = userService;
		this.bookService = bookService;
	}

	// OutcomeRecords

	@RequestMapping(value = "/outcomes", method = RequestMethod.GET)
	public String allOutcomes(Model model) {
		logger.info("All outcomes page");

		model.addAttribute("outcomes", outcomeService.getAllOutcomeRecords());

		return "admin/outcomes";
	}

	@RequestMapping(value = "/outcome", method = RequestMethod.GET)
	public String createOutcome(Model model,
			@RequestParam(value = "book_id", required = false) Integer book_id) {
		logger.info("Creating OutcomeRecord");

		OutcomeRecord or = new OutcomeRecord();
		Book book = new Book();
		if (book_id != null)
			book.setId(book_id);
		or.setBook(book);
		or.setUser(new User());
		or.setStartDate(new Date());
		model.addAttribute("outcomeRecord", or);

		return "admin/newoutcome";
	}

	@RequestMapping(value = "/outcome", method = RequestMethod.POST)
	public String addOutcomeRecordFromForm(Model model,
			OutcomeRecord outcomeRecord, BindingResult bindingResult) {
		logger.info("Add OutcomeRecord");

		if (bindingResult.hasErrors()) {
			if (bindingResult.getFieldError("returnDate") != null) {

				bindingResult.addError(new ObjectError("returnDate",
						"Incorrect returnDate format"));
			}

			return "admin/newoutcome";
		}

		if (!userService.userCanGetBook(outcomeRecord.getUser())) {
			bindingResult.addError(new FieldError(
					bindingResult.getObjectName(), "user",
					"This user can't get the book"));
			return "admin/newoutcome";
		}

		if (!bookService.bookIsAvailable(outcomeRecord.getBook())) {
			bindingResult.addError(new FieldError(
					bindingResult.getObjectName(), "book",
					"This book is not available"));
			return "admin/newoutcome";
		}

		Date sd = outcomeRecord.getStartDate();
		Date rd = outcomeRecord.getReturnDate();
		if (rd == null || rd.before(sd)) {
			bindingResult.addError(new ObjectError("returnDate",
					"Return date must be after Start date"));
			return "admin/newoutcome";
		}

		outcomeService.addOutcomeRecord(outcomeRecord);
		outcomeRecord = outcomeService.find(outcomeRecord);
		model.addAttribute("outcome", outcomeRecord);
		model.addAttribute("price", outcomeService.countPrice(outcomeRecord));
		logger.info("Outcome added -> go to Summary");
		return "admin/outcome_summary";
	}

	@RequestMapping(value = "/delete_outcome", method = RequestMethod.GET, params = "id")
	public String deleteOutcome(@RequestParam(value = "id") Integer id) {
		outcomeService.deleteOutcomeRecordById(id);
		return "redirect:outcome/";
	}

	// IncomeRecords

	@RequestMapping(value = "/incomes", method = RequestMethod.GET)
	public String allIncomes(Model model) {
		logger.info("All incomes page");

		model.addAttribute("incomes", incomeService.getAllIncomeRecords());

		return "admin/incomes";
	}

	@RequestMapping(value = "/income", method = RequestMethod.GET)
	public String createIncome(Model model,
			@RequestParam(value = "order_id", required = false) Integer order_id) {
		logger.info("Creating IncomeRecord");

		IncomeRecord ir = new IncomeRecord();
		ir.setDamageFine(new Double(0));
		ir.setDelayFine(new Double(0));
		OutcomeRecord or = new OutcomeRecord();
		if (order_id != null)
			or.setId(order_id);
		ir.setOutcome(or);
		ir.setRealReturnDate(new Date());
		model.addAttribute("incomeRecord", ir);

		return "admin/newincome";
	}

	@RequestMapping(value = "/income", method = RequestMethod.POST)
	public String addIncomeRecordFromForm(Model model,
			IncomeRecord incomeRecord, BindingResult bindingResult) {
		logger.info("Add IncomeRecord");

		if (bindingResult.hasErrors()) {
			return "admin/newincome";
		}

		if (!outcomeService.isRecordClosable(incomeRecord.getOutcome())) {
			bindingResult.addError(new FieldError(
					bindingResult.getObjectName(), "outcome",
					"This order can't be closed"));
			incomeRecord.setRealReturnDate(null);
			return "admin/newincome";
		}

		Date sd = outcomeService.find(incomeRecord.getOutcome()).getStartDate();
		Date rrd = incomeRecord.getRealReturnDate();
		if (rrd.before(sd)) {
			bindingResult.addError(new ObjectError("realReturnDate",
					"Real return date must be after Start Date"));
			return "admin/newincome";
		}

		incomeService.addIncomeRecord(incomeRecord);
		incomeRecord = incomeService.find(incomeRecord);
		incomeService.countDelayFine(incomeRecord);
		incomeRecord = incomeService.find(incomeRecord);
		int delay = incomeService.getDelayDays(incomeRecord);
		model.addAttribute("income", incomeRecord);
		model.addAttribute("delay", delay);
		logger.info("IncomeRecord created -> Go to summary");

		return "admin/income_summary";
	}

	@RequestMapping(value = "/delete_income", method = RequestMethod.GET, params = "id")
	public String deleteIncome(@RequestParam(value = "id") Integer id) {
		incomeService.deleteIncomeRecordById(id);
		return "redirect:income/";
	}

	// Admin Page
	@RequestMapping(method = RequestMethod.GET)
	public String adminpage(Model model) {
		logger.info("Welcome to homepage");

		return "admin";
	}

	// Books
	@RequestMapping(value = "/books", method = RequestMethod.GET)
	public String books(Model model) {
		logger.info("Welcome to admin books page!");

		Map<BookClass, Integer> books = bookService
				.getBooksWithAvailableCount();
		model.addAttribute("books", books);

		return "admin/books";
	}

	@RequestMapping(value = "/book/{bclassId}", method = RequestMethod.GET)
	public String book(Model model, @PathVariable int bclassId) {
		logger.info("admin Book page");

		BookClass bclass = bookService.getBookClassById(bclassId);
		model.addAttribute("bookClass", bclass);
		List<Book> books = bookService.getAvailableBooksForClass(bclass);
		model.addAttribute("books", books);

		return "admin/book";
	}

	// Users
	@RequestMapping(value = "/users", method = RequestMethod.GET)
	public String users(Model model) {
		logger.info("All Users");	
		return partUsers(model,1,5);
	}

	@RequestMapping(value = "/users", method = RequestMethod.GET, params={"pageNum","perPage"})
	public String partUsers(Model model,
			@RequestParam(value = "pageNum") Integer pageNum,
			@RequestParam(value = "perPage") Integer perPage) {
		logger.info("Paging users");

		List<User> users = userService.findPart(pageNum, perPage);
		Long totalCount = userService.recordCount();
		Long maxPage = totalCount/perPage+1;
		logger.info("MaxPage ="+maxPage);
		logger.info("Total Count ="+totalCount);
		logger.info("PageNum ="+pageNum);
		
		model.addAttribute("users", users);
		model.addAttribute("perPage",perPage);
		model.addAttribute("prevNum",(pageNum>1)?pageNum-1:1);
		model.addAttribute("nextNum",(pageNum<maxPage)?pageNum+1:pageNum);
		
		return "admin/users";
	}

	@RequestMapping(value = "/user/{userId}", method = RequestMethod.GET)
	public String user(Model model, @PathVariable int userId) {
		logger.info("Welcome to admin user page!");

		User user = userService.findUserById(userId);
		model.addAttribute("user", user);
		List<OutcomeRecord> orlist = outcomeService
				.getActiveOrdersForUser(user);
		model.addAttribute("orders", orlist);

		return "admin/user";
	}

	@RequestMapping(value = "/users", method = RequestMethod.GET, params = "find")
	public String foundedUsers(Model model,
			@RequestParam(value = "find") String find) {
		logger.info("Search Result");
		List<User> list = userService.searchUser(find);
		model.addAttribute("users", list);
		model.addAttribute("pagination",false);
		return "admin/users";
	}
}
